﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using LaunchMeNot.DAL;
using LaunchMeNot.Properties;
using Lime49;
using Lime49.Exceptions;
using Lime49.UI;
using Lime49.WPF;
using LaunchMeNot.Core;
using Microsoft.Windows.Controls;
using System.IO;
using System.Diagnostics;

namespace LaunchMeNot
{
    /// <summary>
    /// 
    /// </summary>
    public partial class WinMain : Window
    {
        private BackgroundWorker _worker;
        private static readonly DependencyProperty LaunchMeNotEntriesProperty = DependencyProperty.Register("LaunchMeNotEntries", typeof(ObservableCollection<LaunchMeNotEntry>), typeof(WinMain), new PropertyMetadata(new ObservableCollection<LaunchMeNotEntry>()));
        private ObservableCollection<LaunchMeNotEntry> LaunchMeNotEntries
        {
            get
            {
                return base.GetValue(LaunchMeNotEntriesProperty) as ObservableCollection<LaunchMeNotEntry>;
            }
            set
            {
                base.SetValue(LaunchMeNotEntriesProperty, value);
            }
        }

        public WinMain() {
            InitializeComponent();
        }

        /// <summary>
        /// Loads available entries when the Window Loads.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            NavigationCommands.Refresh.Execute(null, this);
            
        }

        #region CRUD
        /// <summary>
        /// Shows a Window to add an entry.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void AddEntry(object sender, ExecutedRoutedEventArgs e)
        {
            WinAddEntry dlg = new WinAddEntry() { Owner = this };
            dlg.RequestSave += (newEntry) =>
            {
                if (newEntry is LaunchMeNotEntry) {
                    ((LaunchMeNotEntry)newEntry).LaunchOrder = lstLaunchMeNot.Items.Count;
                }
                return AddEntries(new[] { newEntry });
            };
            dlg.ShowDialog();
            /*WinAddEntry dlg = new WinAddEntry(typeof(LaunchMeNotEntry)) { Owner = this };
            if (dlg.ShowDialog() == true) {
                LaunchMeNotEntry newEntry = dlg.SelectedEntry as LaunchMeNotEntry;
                newEntry.LaunchOrder = lstLaunchMeNot.Items.Count;
                AddEntries(new [] { newEntry }, StartupEntryType.LaunchMeNot);
            }*/
        }

        /// <summary>
        /// Adds new entries.
        /// </summary>
        /// <param name="entries">The entries.</param>
        /// <param name="entryType">Type of the entries.</param>
        /// <returns>
        /// 	<c>true</c> if the item was added, <c>false</c> if there were errors, <c>null</c> if the user cancelled.
        /// </returns>
        private bool AddEntries(IEnumerable<IStartupEntry> entries)
        {
            bool success = false;
            WaitForWorker();
            _worker = new BackgroundWorker();
            _worker.DoWork += (s, dwe) =>
            {
                LaunchMeNotBLL.AddStartupEntries(entries);
            };
            _worker.RunWorkerCompleted += (s, rwe) =>
            {
                SetStatus(null);
                if (rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "ErrorAddingItem"));
                    success = false;
                } else {
                    success = true;
                }
            };
            SetStatus(I18nUtils.GetString("Strings", "Saving"));
            _worker.RunWorkerAsync();
            WaitForWorker();
            NavigationCommands.Refresh.Execute(null, this);
            return success;
        }

        /// <summary>
        /// Shows a dialog to edit an entry.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void EditEntry(object sender, ExecutedRoutedEventArgs e)
        {
            IStartupEntry entry = e.Parameter as IStartupEntry;
            if (entry == null) {
                if (tbcLocations.SelectedItem == tabLaunchMeNot) {
                    entry = lstLaunchMeNot.SelectedItem as IStartupEntry;
                } else if (tbcLocations.SelectedItem == tabWindowsEntries) {
                    entry = windowsEntryGrid.SelectedEntry;
                }
            }
            if (entry == null)
                return;
            WinAddEntry dlg = new WinAddEntry(entry.Clone()) { Owner = this };
            dlg.RequestSave += (newEntry) => EditEntry(newEntry, true);
            dlg.ShowDialog();
        }

        /// <summary>
        /// Edits an entry.
        /// </summary>
        /// <param name="entry">The entry.</param>
        /// <param name="refreshOnSuccess"><c>true</c> if the entry list should be refresh on successful committing of the edit.</param>
        /// <returns></returns>
        private bool EditEntry(IStartupEntry entry, bool refreshOnSuccess)
        {
            bool success = false;
            WaitForWorker();
            _worker = new BackgroundWorker();
            _worker.DoWork += (s, dwe) =>LaunchMeNotBLL.EditStartupEntry(entry);
            _worker.RunWorkerCompleted += (s, rwe) =>
            {
                SetStatus(null);
                tbcLocations.IsEnabled = true;
                if (rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "ErrorSaving"));
                } else {
                    success = true;
                }
            };
            SetStatus(I18nUtils.GetString("Strings", "Saving"));
            tbcLocations.IsEnabled = false;
            _worker.RunWorkerAsync();
            WaitForWorker();
            CommandManager.InvalidateRequerySuggested();
            if (success && refreshOnSuccess) {
                NavigationCommands.Refresh.Execute(null, this);
            }
            return success;
        }

        /// <summary>
        /// Duplicates an entry.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void CreateCopy(object sender, ExecutedRoutedEventArgs e)
        {
            IStartupEntry entry = e.Parameter as IStartupEntry;
            WaitForWorker();
            _worker = new BackgroundWorker();
            _worker.DoWork += (s, dwe) =>
            {
                if (entry is LaunchMeNotEntry) {
                    LaunchMeNotEntry actualEntry = entry as LaunchMeNotEntry;
                    actualEntry.ID = Lime49.Utils.GenID(true);
                    actualEntry.Name = I18nUtils.GetString("Strings", "CopyOfFmt", actualEntry.Name);
                    DALConfigurationManager.LaunchMeNotStore.AddStartupEntry(actualEntry);
                } else {
                    IStartupEntry newEntry = entry.Clone();
                    newEntry.Name = LaunchMeNot.Core.Utils.GetNextAvailableName(entry.Name, windowsEntryGrid.StartupEntries.Select(theEntry => theEntry.Name));
                    LaunchMeNotBLL.AddStartupEntries(new[] { newEntry });
                }
            };
            _worker.RunWorkerCompleted += (s, rwe) =>
            {
                SetStatus(null);
                tbcLocations.IsEnabled = true;
                if (rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "ErrorSaving"));
                }
            };
            SetStatus(I18nUtils.GetString("Strings", "Saving"));
            tbcLocations.IsEnabled = false;
            _worker.RunWorkerAsync();
            WaitForWorker();
            NavigationCommands.Refresh.Execute(null, this);
        }

        /// <summary>
        /// Deletes an entry after confirming with a prompt.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void DeleteEntry(object sender, ExecutedRoutedEventArgs e)
        {
            IEnumerable<IStartupEntry> selectedEntries = null;
            if(e.Parameter is IStartupEntry) {
                selectedEntries = new[] {e.Parameter as IStartupEntry};
            } else if(tbcLocations.SelectedItem == tabLaunchMeNot) {
                selectedEntries = lstLaunchMeNot.SelectedItems.OfType<IStartupEntry>();
            } else if(tbcLocations.SelectedItem == tabWindowsEntries) {
                selectedEntries = windowsEntryGrid.SelectedEntries.OfType<IStartupEntry>();
            }
            if(selectedEntries == null || !selectedEntries.Any())
                return;

            //todo: rewrite this to check selected tab once
            
            if (tbcLocations.SelectedItem == tabLaunchMeNot) {
                DeleteLaunchMeNotEntries(selectedEntries.Cast<LaunchMeNotEntry>());
            } else if (tbcLocations.SelectedItem == tabWindowsEntries) {
                DeleteEntries(selectedEntries);
            }
        }

        /// <summary>
        /// Deletes generic startup entries.
        /// </summary>
        /// <param name="selectedEntries">The selected entries.</param>
        private void DeleteEntries(IEnumerable<IStartupEntry> selectedEntries)
        {
            string message;
            string caption;
            if (selectedEntries.Count() == 1) {
                message = I18nUtils.GetString("Strings", "RemoveEntryPromptFmt", selectedEntries.First().Name);
                caption = I18nUtils.GetString("Strings", "RemoveEntry");
            } else {
                message = I18nUtils.GetString("Strings", "RemoveEntriesPrompt");
                caption = I18nUtils.GetString("Strings", "RemoveEntries");
            }
            if (DialogBox.Show(this, message, caption, DialogBoxType.YesNo, DialogBoxIcon.Question, DialogBoxButton.No) == DialogBoxButton.Yes) {
                WaitForWorker();
                _worker = new BackgroundWorker();
                _worker.DoWork += (s, dwe) =>LaunchMeNotBLL.DeleteStartupEntries(selectedEntries);
                _worker.RunWorkerCompleted += (s, rwe) =>
                {
                    SetStatus(null);
                    tbcLocations.IsEnabled = true;
                    if (rwe.Error != null) {
                        DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "ErrorDeleting"));
                    }
                    NavigationCommands.Refresh.Execute(null, this);
                };
                tbcLocations.IsEnabled = false;
                SetStatus(I18nUtils.GetString("Strings", "Deleting"));
                _worker.RunWorkerAsync();
            }
        }

        /// <summary>
        /// Deletes entries managed by LaunchMeNot.
        /// </summary>
        /// <param name="entries">The entries.</param>
        private void DeleteLaunchMeNotEntries(IEnumerable<LaunchMeNotEntry> entries)
        {
            string message,
                   caption,
                   disableButtonText; // if this is null, the 'Disable' button is hidden
            if (entries.Count() == 1) {
                var selectedItem = entries.First();
                caption = I18nUtils.GetString("Strings", "RemoveEntry");
                if (selectedItem.IsEnabled) {
                    // can't disable if already disabled
                    message = I18nUtils.GetString("Strings", "DisableInsteadRemovePromptFmt", selectedItem.Name);
                    disableButtonText = I18nUtils.GetString("Strings", "Disable");
                } else {
                    message = I18nUtils.GetString("Strings", "RemoveEntryPromptFmt", selectedItem.Name);
                    disableButtonText = null;
                }
            } else {
                caption = I18nUtils.GetString("Strings", "RemoveEntries");
                message = I18nUtils.GetString("Strings", "DisableInsteadRemovePrompt");
                disableButtonText = I18nUtils.GetString("Strings", "Disable");
            }
            WaitForWorker();
            _worker = new BackgroundWorker();
            switch (DialogBox.Show(this, message, caption, DialogBoxIcon.Question, DialogBoxButton.Custom1, disableButtonText, I18nUtils.GetString("Strings", "Remove"), I18nUtils.GetString("Strings", "Cancel"))) {
                case DialogBoxButton.Custom1:
                    _worker.DoWork += (s, dwe) =>
                    {
                        foreach (var entry in entries) {
                            entry.IsEnabled = false;
                        }
                        XMLStore store = LaunchMeNotBLL.GetStore(StartupEntryType.LaunchMeNot) as XMLStore;
                        store.EditStartupEntries(entries);
                    };
                    break;
                case DialogBoxButton.Custom2:
                    _worker.DoWork += (s, dwe) => LaunchMeNotBLL.DeleteStartupEntries(entries.Cast<IStartupEntry>());
                    break;
                default:
                    return;
            }
            _worker.RunWorkerCompleted += (s, rwe) =>
            {
                SetStatus(null);
                tbcLocations.IsEnabled = true;
                if (rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "ErrorSaving"));
                }
                NavigationCommands.Refresh.Execute(null, this);
            };
            SetStatus(I18nUtils.GetString("Strings", "Saving"));
            tbcLocations.IsEnabled = false;
            _worker.RunWorkerAsync();
        }

        /// <summary>
        /// Converts an entry to a different StartupEntryType.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ConvertEntry(object sender, ExecutedRoutedEventArgs e)
        {
            ConvertEventArgs args = e.Parameter as ConvertEventArgs;
            if (args == null)
                return;
            WaitForWorker();
            _worker = new BackgroundWorker();
            _worker.DoWork += (s, dwe) => LaunchMeNotBLL.ConvertEntry(args.Entry, args.TargetType);
            _worker.RunWorkerCompleted += (s, rwe) =>
            {
                SetStatus(null);
                tbcLocations.IsEnabled = true;
                if (rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "ErrorSaving"));
                }
                CommandManager.InvalidateRequerySuggested();
                NavigationCommands.Refresh.Execute(null, this);
            };
            SetStatus(I18nUtils.GetString("Strings", "Saving"));
            tbcLocations.IsEnabled = false;
            _worker.RunWorkerAsync();
            WaitForWorker();
            CommandManager.InvalidateRequerySuggested();
        }

        /// <summary>
        /// Refreshes the entry list.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void RefreshEntries(object sender, ExecutedRoutedEventArgs e)
        {
            WPFConfigurationManager.EnsureDatabaseValid();
            WaitForWorker();
            _worker = new BackgroundWorker();
            _worker.DoWork += (s, dwe) =>
            {
                dwe.Result = new object[] {
                    DALConfigurationManager.LaunchMeNotStore.GetStartupEntries(),
                    LaunchMeNotBLL.IsLaunchMeNotInstalled()
                };
            };
            _worker.RunWorkerCompleted += (s, rwe) =>
            {
                SetStatus(null);
                if (rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "ErrorLoadingEntries"));
                } else if (rwe.Result is object[]) {
                    object[] result = rwe.Result as object[];
                    var launchMeNotResult = (IEnumerable<IStartupEntry>)result[0];
                    this.LaunchMeNotEntries = new ObservableCollection<LaunchMeNotEntry>(launchMeNotResult.Cast<LaunchMeNotEntry>().OrderBy(i => i.LaunchOrder));
                    tbcLocations.IsEnabled = true;

                    StartupEntryType installState = (StartupEntryType)result[1];
                    itmInstallAllUsersStartup.IsChecked = false;
                    itmInstallCurrentUserStartup.IsChecked = false;
                    itmDisableInstall.IsChecked = false;
                    switch (installState) {
                        case StartupEntryType.CurrentUserRun:
                            itmInstallCurrentUserStartup.IsChecked = true;
                            break;
                        case StartupEntryType.LocalMachineRun:
                            itmInstallAllUsersStartup.IsChecked = true;
                            break;
                        case StartupEntryType.None:
                            itmDisableInstall.IsChecked = true;
                            break;
                    }
                }
                CommandManager.InvalidateRequerySuggested();
            };
            SetStatus(I18nUtils.GetString("Strings", "Loading"));
            tbcLocations.IsEnabled = false;
            _worker.RunWorkerAsync();
            NavigationCommands.Refresh.Execute(null, windowsEntryGrid);
            WaitForWorker();
            CommandManager.InvalidateRequerySuggested();
        }

        private void CanMoveUp(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = IsLoaded && e.Parameter != null && lstLaunchMeNot.SelectedIndex > 0;
        }

        /// <summary>
        /// Moves the selected item up.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void MoveUp(object sender, ExecutedRoutedEventArgs e)
        {
            LaunchMeNotEntry idx = lstLaunchMeNot.SelectedItem as LaunchMeNotEntry;
            int currentPos = lstLaunchMeNot.Items.IndexOf(idx);
            ObservableCollection<LaunchMeNotEntry> newEntries = new ObservableCollection<LaunchMeNotEntry>(LaunchMeNotEntries.Select(entry => entry.Clone())
                                                                                                                             .Cast<LaunchMeNotEntry>());
            newEntries.RemoveAt(currentPos);
            newEntries.Insert(currentPos - 1, idx);
            for (int i = 0; i < newEntries.Count; i++) {
                newEntries[i].LaunchOrder = i;
            }
            LaunchMeNotEntries = newEntries;
            SaveEntryLaunchOrder(newEntries);
        }

        private void CanMoveDown(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = IsLoaded && e.Parameter != null && lstLaunchMeNot.SelectedIndex < lstLaunchMeNot.Items.Count - 1 && lstLaunchMeNot.SelectedIndex >= 0;
        }

        /// <summary>
        /// Moves the selected item down.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void MoveDown(object sender, ExecutedRoutedEventArgs e)
        {
            LaunchMeNotEntry idx = lstLaunchMeNot.SelectedItem as LaunchMeNotEntry;
            int currentPos = lstLaunchMeNot.Items.IndexOf(idx);
            ObservableCollection<LaunchMeNotEntry> newEntries = new ObservableCollection<LaunchMeNotEntry>(LaunchMeNotEntries.Select(entry => entry.Clone())
                                                                                                                             .Cast<LaunchMeNotEntry>());
            newEntries.RemoveAt(currentPos);
            newEntries.Insert(currentPos + 1, idx);
            for (int i = 0; i < newEntries.Count; i++) {
                newEntries[i].LaunchOrder = i;
            }
            LaunchMeNotEntries = newEntries;
            SaveEntryLaunchOrder(newEntries);
        }

        /// <summary>
        /// Saves items to the database.
        /// </summary>
        /// <param name="entries">The list of entries.</param>
        private void SaveEntryLaunchOrder(IEnumerable<LaunchMeNotEntry> entries)
        {
            WaitForWorker();
            _worker = new BackgroundWorker();
            _worker.DoWork += (s, dwe) => DALConfigurationManager.LaunchMeNotStore.EditStartupEntries(entries);
            _worker.RunWorkerCompleted += (s, rwe) =>
            {
                SetStatus(null);
                if (rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "ErrorSaving"));
                }
            };
            SetStatus(I18nUtils.GetString("Strings", "Saving"));
            _worker.RunWorkerAsync();
            WaitForWorker();
            CommandManager.InvalidateRequerySuggested();
        }
        #endregion

        #region Datagrid Specific
        /// <summary>
        /// Commits an edit to the database.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.Windows.Controls.DataGridCellEditEndingEventArgs"/> instance containing the event data.</param>
        private void dg_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
        {
            var entry = e.Row.DataContext as LaunchMeNotEntry;
            if (entry == null)
                return;
            EditEntry(entry, false);
        }

        /// <summary>
        /// Single click editing (changing of enabled state)
        /// </summary>
        private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DataGridCell cell = sender as DataGridCell;
            if (cell != null && !cell.IsEditing && !cell.IsReadOnly) {
                if (!cell.IsFocused) {
                    cell.Focus();
                }
                DataGrid dataGrid = WPFUtils.FindVisualParent<DataGrid>(cell);
                if (dataGrid != null) {
                    if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow) {
                        if (!cell.IsSelected)
                            cell.IsSelected = true;
                    } else {
                        DataGridRow row = WPFUtils.FindVisualParent<DataGridRow>(cell);
                        if (row != null && !row.IsSelected) {
                            row.IsSelected = true;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Allows editing on double click.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void dgCell_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var cell = sender as DataGridCell;
            if (cell == null)
                return;
            ApplicationCommands.Properties.Execute(cell.DataContext, this);
        }

        /// <summary>
        /// Allows deletion when pressing the Delete key.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.KeyEventArgs"/> instance containing the event data.</param>
        private void lstLaunchMeNot_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Delete) {
                List<LaunchMeNotEntry> selectedEntries = new List<LaunchMeNotEntry>();
                foreach (var item in lstLaunchMeNot.SelectedItems) {
                    if (item is LaunchMeNotEntry)
                        selectedEntries.Add((LaunchMeNotEntry)item);
                }
                ApplicationCommands.Delete.Execute(selectedEntries, lstLaunchMeNot);
            }
        }
        #endregion

        #region Misc Dialogs
        /// <summary>
        /// Starts the AutoLauncher.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void AutoLaunch(object sender, ExecutedRoutedEventArgs e)
        {
            WinLaunch dlg = new WinLaunch() { Owner = this };
            dlg.ShowDialog();
        }

        /// <summary>
        /// Shows the 'About' dialog.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowAbout(object sender, ExecutedRoutedEventArgs e) {
            WinAbout dlg = new WinAbout() {Owner = this};
            dlg.ShowDialog();
        }

        /// <summary>
        /// Shows the options dialog.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowOptions(object sender, ExecutedRoutedEventArgs e)
        {
            WinOptions dlg = new WinOptions() { Owner = this };
            dlg.ShowDialog();
            NavigationCommands.Refresh.Execute(null, this);
        }

        /// <summary>
        /// Opens the update checker.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void CheckForUpdate(object sender, ExecutedRoutedEventArgs e)
        {
            WinUpdater dlg = new WinUpdater() { Owner = this };
            dlg.ShowDialog();
        }
        #endregion

        /// <summary>
        /// Enables or disables AutoLaunch for the current user or all users.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ToggleInstall(object sender, ExecutedRoutedEventArgs e)
        {
            BackgroundWorker worker = new BackgroundWorker();
            StartupEntryType requiredType = (StartupEntryType)Enum.Parse(typeof(StartupEntryType), (e.Parameter ?? "0").ToString(), true);
            worker.DoWork += (s, dwe) => LaunchMeNotBLL.ToggleAutoLaunch(requiredType);
            worker.RunWorkerCompleted += (s, rwe) =>
            {
                SetStatus(null);
                if (rwe.Error != null) {
                    DialogBox.ShowAlert(this, rwe.Error, I18nUtils.GetString("Errors", "ErrorInstalling"));
                }
            };
            SetStatus(I18nUtils.GetString("Strings", "Saving"));
            worker.RunWorkerAsync();
            LaunchMeNot.Core.Utils.WaitForWorker(worker);
            NavigationCommands.Refresh.Execute(null, this);
        }

        #region Misc
        private void CloseWindow(object sender, ExecutedRoutedEventArgs e)
        {
            Close();
        }

        /// <summary>
        /// Opens the explorer (shell) properties dialog for a file.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void OpenExplorerProperties(object sender, ExecutedRoutedEventArgs e)
        {
            IStartupEntry entry = e.Parameter as IStartupEntry;
            if (entry == null)
                return;
            try {
                if (!File.Exists(entry.Path))
                    throw new FileNotFoundException(entry.Path);
                LaunchMeNot.Core.Utils.ShowFileProperties(entry.Path);
            } catch (FileNotFoundException ex) {
                Console.WriteLine(ex.Message);
                DialogBox.ShowAlert(this, I18nUtils.GetString("Errors", "FileDoesNotExist"), I18nUtils.GetString("Errors", "CouldNotOpenFile"));
            } catch (Exception ex) {
                Console.WriteLine(ex.Message);
                DialogBox.ShowAlert(this, I18nUtils.GetString("Errors", "CouldNotOpenFile") + ": " + ex.Message, I18nUtils.GetString("Errors", "Error"));
            }
        }

        /// <summary>
        /// Opens the location of an entry.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void OpenFileLocation(object sender, ExecutedRoutedEventArgs e) {
            IStartupEntry entry = e.Parameter as IStartupEntry;
            if(entry == null)
                return;
            try {
                FileInfo info = new FileInfo(entry.Path);
                if(!info.Directory.Exists)
                    throw new FileNotFoundException(info.Directory.FullName);
                Process.Start(info.Directory.FullName);
            } catch(FileNotFoundException ex) {
                Console.WriteLine(ex.Message);
                DialogBox.ShowAlert(this, I18nUtils.GetString("Errors", "FileDoesNotExist"), I18nUtils.GetString("Errors", "CouldNotOpenDirectory"));
            } catch(Exception ex) {
                Console.WriteLine(ex.Message);
                DialogBox.ShowAlert(this, I18nUtils.GetString("Errors", "CouldNotOpenDirectory") + ": " + ex.Message, I18nUtils.GetString("Errors", "Error"));
            }
        }

        /// <summary>
        /// Saves user preferences.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void SaveSettings(object sender, RoutedEventArgs e)
        {
            Settings.Default.MainWindowAOT = this.Topmost;
            Settings.Default.Save();
        }

        /// <summary>
        /// Sets the text on the status message (null hides the statusbar and label).
        /// </summary>
        /// <param name="message">The message.</param>
        private void SetStatus(string message)
        {
            if (message == null) {
                prgStatus.Visibility = Visibility.Collapsed;
                lblStatus.Visibility = Visibility.Collapsed;
            } else {
                prgStatus.Visibility = Visibility.Visible;
                lblStatus.Visibility = Visibility.Visible;
                lblStatus.Text = message;
            }
        }

        /// <summary>
        /// Shows the help file.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowHelp(object sender, ExecutedRoutedEventArgs e)
        {
            LaunchMeNot.Core.Utils.OpenHelp(null);
        }

        /// <summary>
        /// Opens the default browser to search for process info.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void SearchOnline(object sender, ExecutedRoutedEventArgs e)
        {
            string searchUrl = Convert.ToString(e.Parameter);
            if (string.IsNullOrEmpty(searchUrl))
                return;
            try {
                System.Diagnostics.Process.Start(searchUrl.Replace("%1", searchUrl));
            } catch (Exception ex) {
                DialogBox.ShowAlert(this, ex.Message, I18nUtils.GetString("Errors", "Error"));
            }
        }

        private void CanAlwaysExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        /// <summary>
        /// Determines whether either an item is selected.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void IsItemSelected(object sender, CanExecuteRoutedEventArgs e)
        {
            if (!IsLoaded)
                return;
            if (tbcLocations.SelectedItem == tabLaunchMeNot) {
                e.CanExecute = lstLaunchMeNot != null && lstLaunchMeNot.SelectedItems.Count > 0;
            } else if (tbcLocations.SelectedItem == tabWindowsEntries) {
                e.CanExecute = windowsEntryGrid.SelectedEntries.Count > 0;
            }
        }

        /// <summary>
        /// Determines whether the CommandParameter is null.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void IsParameterNull(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = IsLoaded && e.Parameter != null;
        }

        /// <summary>
        /// Determines whether the BackgroundWorker is busy.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void IsWorkerBusy(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = IsLoaded && (_worker == null || !_worker.IsBusy||true);
        }

        /// <summary>
        /// Waits for the BackgroundWorker to complete before returning.
        /// </summary>
        private void WaitForWorker()
        {
            LaunchMeNot.Core.Utils.WaitForWorker(_worker);
            CommandManager.InvalidateRequerySuggested();
        }
        #endregion

        #region Drag and Drop
        /// <summary>
        /// Allows dropping of files and text onto the Window.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.DragEventArgs"/> instance containing the event data.</param>
        private void Window_DragEnter(object sender, DragEventArgs e) {
            if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent(DataFormats.Text) || e.Data.GetDataPresent(DataFormats.UnicodeText)) {
                e.Effects = DragDropEffects.Copy;
            } else {
                e.Effects = DragDropEffects.None;
            }
        }

        /// <summary>
        /// Prompts to add new entries on drop.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.DragEventArgs"/> instance containing the event data.</param>
        private void Window_Drop(object sender, DragEventArgs e) {
            try {
                if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
                    string[] fileNames = (string[])e.Data.GetData(DataFormats.FileDrop);
                    List<IStartupEntry> entries = new List<IStartupEntry>();
                    MultipleException errors = new MultipleException();
                    WinChooseEntryType dlgChooseType = new WinChooseEntryType(I18nUtils.GetString("Strings", "HowShouldTheseEntriesBeManaged"), I18nUtils.GetString("Strings", "SelectStartupType")) { Owner = this };
                    if (dlgChooseType.ShowDialog() == true) {
                        foreach(string path in fileNames) {
                            try {
                                switch (dlgChooseType.SelectedType) {
                                    case StartupEntryType.AllUsersStartup:
                                    case StartupEntryType.CurrentUserStartup:
                                        entries.Add(new ShellShortcut(path));
                                        break;
                                    case StartupEntryType.CurrentUserRun:
                                    case StartupEntryType.LocalMachineRun:
                                        entries.Add(new RegistryShortcut(path));
                                        break;
                                    case StartupEntryType.LaunchMeNot:
                                        entries.Add(new LaunchMeNotEntry(path));
                                        break;
                                }
                            } catch (Exception ex) {
                                errors.Exceptions.Add(new Exception(I18nUtils.GetString("Errors", "ErrorSavingNameFmt", path, ex.Message), ex));
                            }
                        }
                        foreach (var entry in entries) {
                            entry.EntryType = dlgChooseType.SelectedType;
                        }
                        AddEntries(entries);
                        if(errors.Exceptions.Any())
                            DialogBox.ShowAlert(this, errors, I18nUtils.GetString("Errors", "ErrorAddingSomeItems"));
                    }
                } else if (e.Data.GetDataPresent(DataFormats.Text) || e.Data.GetDataPresent(DataFormats.UnicodeText)) {
                    string draggedText = (e.Data.GetData(DataFormats.Text) ?? e.Data.GetData(DataFormats.UnicodeText)) as string;
                    WinAddEntry dlg = new WinAddEntry(typeof(LaunchMeNotEntry)) { Owner = this };
                    dlg.SelectedEntry.Path = draggedText;
                    if (dlg.ShowDialog() == true) {
                        LaunchMeNotEntry newEntry = dlg.SelectedEntry as LaunchMeNotEntry;
                        newEntry.LaunchOrder = lstLaunchMeNot.Items.Count;
                        AddEntries(new[] {newEntry});
                    }
                }
            } catch (Exception ex) {
                DialogBox.ShowAlert(this, ex.Message, I18nUtils.GetString("Errors", "Error"));
            }
        }
        #endregion
    }
}